//
//  xsd2cocoaTests.m
//  xsd2cocoaTests
//
//  Created by Stefan Winter on 5/21/11.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import "xsd2cocoaTests.h"
#import "TemporaryDirectoryCategory.h"
#import "XSDschema.h"
#import "XSDcomplexType.h"
#import "TSType2.h"
#import "RIItem.h"

@implementation xsd2cocoaTests

- (void)setUp
{
    [super setUp];
    
    // Set-up code here.
}

- (void)tearDown
{
    // Tear-down code here.
    
    [super tearDown];
}

- (void)testTemplateGeneration
{
    NSBundle* testBundle = [NSBundle bundleForClass:[xsd2cocoaTests class]];
//    NSBundle* bundle = [NSBundle bundleForClass:[XSDschema class]];
    NSURL *schemaURL = [testBundle URLForResource:@"testSchema01" withExtension:@"xsd"];
    NSError* error = nil;
    XSDschema* schema = [XSDschema newWithUrl: schemaURL prefix: @"TS" error: &error];
    STAssertNotNil(schema, @"schema must be loaded");
    STAssertEqualObjects(@"http://www.example.org/testSchema01", schema.targetNamespace, @"targetNamespace must be read");
    STAssertEquals(4, (int) schema.complexTypes.count, @"there must be 3 types");

    XSDcomplexType* type1 = [schema.complexTypes objectAtIndex: 0];
    STAssertEqualObjects(@"Type1", type1.name, @"name set");
    
    NSArray* elements = [type1 elements];
    STAssertEquals(3, (int) elements.count, @"there must be 3 elements");
    STAssertEqualObjects(@"element1", [[elements objectAtIndex: 0] name], @"name of element");
    STAssertEqualObjects(@"string", [[elements objectAtIndex: 0] type], @"type of element");
    
    XSDcomplexType* anonymousType = [schema.complexTypes objectAtIndex: 1];
    STAssertEqualObjects(@"AnonymousTypeElement", anonymousType.name, @"name set");
    
    XSDcomplexType* type2 = [schema.complexTypes objectAtIndex: 2];
    STAssertEqualObjects(@"Type2", type2.name, @"name set");
    STAssertEqualObjects(@"TSType1", [type2 baseClassName], @"base class resolved");
    
    STAssertEquals(2, (int) [type2 elements].count, @"there must be 1 element");
    STAssertEqualObjects(@"string", [[[type2 elements] objectAtIndex: 0] type], @"type of element");
    STAssertEqualObjects(@"TSAnonymousTypeElement", [[[type2 elements] objectAtIndex: 1] objcType], @"type of element");
    
    STAssertEquals(1, (int) [type2 attributes].count, @"there must be 1 attribute");
    
    STAssertEquals(1, (int) [type2 globalElements].count, @"there must be 1 globalElement");
    
    // Get path to template.
	NSURL* templatePath = [testBundle URLForResource:@"standard-template" withExtension: @"xml"];
    [schema loadTemplate: templatePath error: &error];
    STAssertEqualObjects(@"NSString", [[schema typeForName: @"string"] targetClassName], @"string = NSString");
    
    NSFileManager* fm = [NSFileManager defaultManager];
    NSURL* tmpOutFolder = [NSURL fileURLWithPath: [fm createTemporaryDirectory] isDirectory: TRUE];
    
    
    [schema generateInto: tmpOutFolder error: &error];
    NSLog(@"%@", error);
    STAssertNil(error, @"no error should be returned");
    
    NSArray* files = [fm contentsOfDirectoryAtURL: tmpOutFolder includingPropertiesForKeys:nil options: NSDirectoryEnumerationSkipsSubdirectoryDescendants error: &error];
    NSLog(@"Processed template:\r%@", files);

    STAssertEquals(8, (int) files.count, @"there must be 6 files");
    
    [fm removeItemAtPath: [tmpOutFolder path] error: &error];
}

- (void) assertObjectValues: (TSType2*) type1 {
    NSDateFormatter* dateFormatter = [[[NSDateFormatter alloc] init] autorelease]; 
    dateFormatter.timeStyle = NSDateFormatterFullStyle;
    dateFormatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss'Z'";
    NSDate* testDate = [dateFormatter dateFromString: @"2004-06-23T14:31:04"];
    
    // test attribute values
    STAssertEqualObjects([NSNumber numberWithInt: 987], type1.intAttribute, @"intAttribute set");
    STAssertEqualObjects(@"testAttributeValue", type1.stringAttribute, @"stringAttribute set");
    STAssertEqualObjects([NSNumber numberWithBool: NO], type1.boolAttribute, @"boolAttribute set");
    STAssertEqualObjects(testDate, type1.dateAttribute, @"dateAttribute set");
    
    // test simple element values
    STAssertEqualObjects(@"testElementValue", type1.element1, @"element1 set");
    STAssertEqualObjects([NSNumber numberWithInt: 1234], type1.element2, @"int element set");
    
    STAssertNotNil(type1.complexElement, @"can read a complex element");
    STAssertEqualObjects([NSNumber numberWithBool: YES], type1.complexElement.element1, @"bool element set");
    STAssertEqualObjects(testDate, type1.complexElement.element2, @"date element set");
    
    // extended elements
    STAssertNotNil(type1.element3, @"can read an extended element");
    STAssertEquals(2, (int) type1.element3.count, @"there must be 2 tags");
    
    // anonymous element type
    STAssertNotNil(type1.anonymousTypeElement, @"has to read anonymous element types");
    
    STAssertNotNil(type1.extendedAttribute, @"can read an extended attributes");
}

- (void) assertObjectValuesEmpty: (TSType2*) type1 {
    NSDateFormatter* dateFormatter = [[[NSDateFormatter alloc] init] autorelease]; 
    dateFormatter.timeStyle = NSDateFormatterFullStyle;
    dateFormatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss'Z'";
    NSDate* testDate = [dateFormatter dateFromString: @"2004-06-23T14:31:04"];
    
    // test simple element values
    STAssertNil(type1.element1, @"element1 set");
    STAssertEqualObjects([NSNumber numberWithInt: 1234], type1.element2, @"int element set");
    
    STAssertNotNil(type1.complexElement, @"can read a complex element");
    STAssertEqualObjects([NSNumber numberWithBool: YES], type1.complexElement.element1, @"bool element set");
    STAssertEqualObjects(testDate, type1.complexElement.element2, @"date element set");
    
    // extended elements
    STAssertNotNil(type1.element3, @"can read an extended element");
    STAssertEquals(3, (int) type1.element3.count, @"there must be 3 tags");
    STAssertEqualObjects([NSNull null], [type1.element3 objectAtIndex:0], @"NSNull expected");
    STAssertEqualObjects(@"testArrayValue2", [type1.element3 objectAtIndex:1], @"testArrayValue2 expected");
    STAssertEqualObjects([NSNull null], [type1.element3 objectAtIndex:2], @"NSNull expected");
    
    // anonymous element type
    STAssertNotNil(type1.anonymousTypeElement, @"has to read anonymous element types");
    STAssertEqualObjects(@"testAnonymElement", type1.anonymousTypeElement.test, @"testAnonymElement expected");
}

- (void) testXmlLoadingWithSpaces {
    NSBundle* bundle = [NSBundle bundleForClass:[xsd2cocoaTests class]];
    NSURL *xml1URL = [bundle URLForResource:@"testXml01" withExtension:@"xml"];
    
    NSData* fileData = [NSData dataWithContentsOfURL: xml1URL];
    
    TSType2* type1 = [TSType2 fromGlobalElement: fileData];
    
    STAssertTrue(type1 != nil, @"reader must be loaded");
    [self assertObjectValues: type1];
}

- (void) testXmlLoadingWithoutSpaces {
    NSBundle* bundle = [NSBundle bundleForClass:[xsd2cocoaTests class]];
    NSURL *xml1URL = [bundle URLForResource:@"testXml02" withExtension:@"xml"];
    
    xmlTextReaderPtr reader = xmlReaderForFile([[xml1URL path] cStringUsingEncoding: NSUTF8StringEncoding], "utf-8", 0);
    STAssertTrue(reader != nil, @"reader must be loaded");
    
    int ret = xmlTextReaderRead(reader);
    if(ret == XML_READER_TYPE_ELEMENT) {
        TSType2* type1 = [[TSType2 alloc] initWithReader: reader];
        [self assertObjectValues: type1];
    }
    xmlFreeTextReader(reader);
}

- (void) testLoadingBrokenFile {
    NSBundle* bundle = [NSBundle bundleForClass:[xsd2cocoaTests class]];
    NSURL *xml1URL = [bundle URLForResource:@"noXml1" withExtension:@"txt"];
    NSData* data = [NSData dataWithContentsOfURL:xml1URL];
	TSType2* type1 = [TSType2 fromGlobalElement:data];
	
	STAssertNil(type1, @"on invalid input, returned element must be nil");
}

- (void) logWrittenXmlData:(NSData*)data {
	NSLog(@"written XML data:\n========\n%@\n========\n", [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]);
}

- (void) testXmlWriting {
    NSBundle* bundle = [NSBundle bundleForClass:[xsd2cocoaTests class]];
    NSURL *xml1URL = [bundle URLForResource:@"testXml01" withExtension:@"xml"];
    
    NSData* fileData = [NSData dataWithContentsOfURL: xml1URL];
    
    TSType2* type1 = [TSType2 fromGlobalElement: fileData];
	
	NSMutableData* writtenData = [NSMutableData data];
	[type1 toGlobalElement:writtenData];
    
	[self logWrittenXmlData:writtenData];
	
    TSType2* type2 = [TSType2 fromGlobalElement: writtenData];
	
    STAssertTrue(type1 != nil, @"could not load written data");
    [self assertObjectValues: type2];
}

- (void) testEmptyXmlTags {
    NSBundle* bundle = [NSBundle bundleForClass:[xsd2cocoaTests class]];
    NSURL *xml1URL = [bundle URLForResource:@"testXml03" withExtension:@"xml"];
    
    NSData* fileData = [NSData dataWithContentsOfURL: xml1URL];
    
    TSType2* type1 = [TSType2 fromGlobalElement: fileData];
	
	NSMutableData* writtenData = [NSMutableData data];
	[type1 toGlobalElement:writtenData];
    
	[self logWrittenXmlData:writtenData];
	
    TSType2* type2 = [TSType2 fromGlobalElement: writtenData];
	
    STAssertTrue(type1 != nil, @"could not load written data");
    [self assertObjectValuesEmpty: type2];
}

- (void) testRecursion {
    NSBundle* bundle = [NSBundle bundleForClass:[xsd2cocoaTests class]];
    NSURL *xml1URL = [bundle URLForResource:@"recursive-items" withExtension:@"xml"];
    
    NSData* fileData = [NSData dataWithContentsOfURL: xml1URL];
    
    RIItem* item1 = [RIItem fromRecursiveItems:fileData];
	
	NSMutableData* writtenData = [NSMutableData data];
	[item1 toRecursiveItems:writtenData];
    
    RIItem* item2 = [RIItem fromRecursiveItems:fileData];
	
    STAssertTrue(item2 != nil, @"could not load written data");
	
	STAssertEqualObjects(@"root", item2.name, @"name must be 'root'");
	STAssertEquals(6, (int)[item2.item count], @"six items expected");
	STAssertEquals(6, (int)[item2.item count], @"six items expected");
	
	RIItem* bar = [item2.item objectAtIndex:4];
	STAssertEqualObjects(@"bar", bar.name, @"recursive child's name should be 'bar'");
	STAssertEquals(3, (int)[bar.item count], @"three child items expected below item 'bar'");
	
	RIItem* r1 = [item2.item objectAtIndex:5];
	RIItem* r2 = [r1.item objectAtIndex:0];
	RIItem* r3 = [r2.item objectAtIndex:0];
	STAssertEqualObjects(@"r3", r3.name, @"recursive child's name should be 'r3'");
}


@end
